package Elevator;

import Passenger.Passenger;

import java.util.Date;
import java.util.LinkedList;
import java.util.Observable;

/**
 * Created by Connor Madden.
 * User: Connor Madden
 * Date: 6/10/11
 * Time: 5:20 PM
 */
public class Elevator extends Observable implements ElevatorI {
    public enum State {
        CLOSING, OPENING, OPEN, STOPPED, MOVING
    }

    private int num, closingTime, openingTime;
    private LinkedList<Passenger> passengers;
    private ElevatorStateI stateObject;
    private Date moveStart;
    private long duration;
    private Thread moveTimeThread;
    private float destination, startMoveLoc, speed;


    public Elevator(int num, float speed, float location) {
        this.num = num;
        stateObject = new ElevatorStateStopped();
        passengers = new LinkedList<Passenger>();
        this.startMoveLoc = location;
        this.speed = speed;
        moveStart = new Date();
    }

    public boolean move(float destination) {
        float location = getLocation();
        float displacement = destination - location;
        if (displacement == 0) {
            return false;
        } else {
            final long duration = ((long) getSpeed() * 1000L) / (long) Math.abs(displacement);
            this.duration = duration;
            startMoveLoc = location;
            this.destination = destination;
            moveStart = new Date();
            Thread moveTimeThread = new Thread(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(duration);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (Thread.currentThread() == getMoveTimeThread()) {
                        stateObject = new ElevatorStateStopped();
                    }
                }
            });
            moveTimeThread.setDaemon(true);
            this.moveTimeThread = moveTimeThread;
            moveTimeThread.start();
            return true;
        }
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public LinkedList<Passenger> getPassengers() {
        return passengers;
    }

    public boolean addPassenger(Passenger passenger) {
        return passengers.add(passenger);
    }

    public boolean removePassenger(Passenger passenger) {
        return passengers.remove(passenger);
    }

    public State getState() {
        return stateObject.getState();
    }

    public void setStateObject(ElevatorStateI stateObject) {
        this.stateObject = stateObject;
        setChanged();
        notifyObservers();
    }

    public int getClosingTime() {
        return closingTime;
    }

    public void setClosingTime(int closingTime) {
        this.closingTime = closingTime;
    }

    public int getOpeningTime() {
        return openingTime;
    }

    public void setOpeningTime(int openingTime) {
        this.openingTime = openingTime;
    }

    public float getLocation() {
        return (float) lerp(startMoveLoc, destination, duration, new Date().getTime() - moveStart.getTime());
    }

    long getDuration() {
        return duration;
    }

    void setDuration(long duration) {
        this.duration = duration;
    }

    float getStartMoveLoc() {
        return startMoveLoc;
    }

    void setStartMoveLoc(float startMoveLoc) {
        this.startMoveLoc = startMoveLoc;
    }

    public float getDestination() {
        return destination;
    }

    void setDestination(float destination) {
        this.destination = destination;
    }

    Date getMoveStart() {
        return moveStart;
    }

    void setMoveStart(Date moveStart) {
        this.moveStart = moveStart;
    }

    Thread getMoveTimeThread() {
        return moveTimeThread;
    }

    void setMoveTimeThread(Thread moveTimeThread) {
        this.moveTimeThread = moveTimeThread;
    }

    public double getSpeed() {
        return speed;
    }

    void setSpeed(float speed) {
        this.speed = speed;
    }

    private static double lerp(float start, float target, double duration, double timeSinceStart) {
        double value = start;
        if (timeSinceStart > 0L && timeSinceStart < duration) {
            final float range = target - start;
            final double percent = timeSinceStart / duration;
            value = start + (range * percent);
        } else if (timeSinceStart >= duration) {
            value = target;
        }
        return value;
    }
}